package me.zhengjie.minbearwcs.serivce.mission.processor.outbound;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.minbearwcs.dao.config.inf.WcsParamConfigDao;
import me.zhengjie.minbearwcs.dao.mission.inf.WcsMissionDao;
import me.zhengjie.minbearwcs.dao.outbound.inf.OutboundTaskRecordDao;
import me.zhengjie.minbearwcs.dao.outbound.inf.OutboundTaskRecordHistoryDao;
import me.zhengjie.minbearwcs.dao.stock.inf.PalletDao;
import me.zhengjie.minbearwcs.dao.storage.inf.SmallStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.StorageDao;
import me.zhengjie.minbearwcs.entity.DTO.agv.common.PointAndPass;
import me.zhengjie.minbearwcs.entity.DTO.agv.task.AgvActionSource;
import me.zhengjie.minbearwcs.entity.DTO.agv.task.AgvTaskSource;
import me.zhengjie.minbearwcs.entity.DTO.agv.task.actionEnum.AgvAction;
import me.zhengjie.minbearwcs.entity.DTO.transfer.Tuple;
import me.zhengjie.minbearwcs.entity.PO.agv.missionEnum.AgvMissionType;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.WcsMission;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.status.OutboundSortExceptionStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.type.MissionType;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.history.OutboundTaskRecordHistory;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.record.OutboundTaskRecord;
import me.zhengjie.minbearwcs.entity.PO.wcs.stock.Pallet;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.SmallStorage;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.type.StorageType;
import me.zhengjie.minbearwcs.resource.WcsResource;
import me.zhengjie.minbearwcs.serivce.agv.task.agvTask.AgvTaskDispatcher;
import me.zhengjie.minbearwcs.serivce.mission.locker.StorageLockCollector;
import me.zhengjie.minbearwcs.serivce.mission.processor.MissionProcessor;
import me.zhengjie.minbearwcs.serivce.mission.processor.MissionProcessorCommonUtil;
import me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor1.Storage1FLocker;
import me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.InboundStorage5Or6FloorLocker;
import me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.OutboundStorage5Or6FloorLocker;
import me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.ShiftingStorage5Or6FloorLocker;
import me.zhengjie.minbearwcs.utils.IdWorker;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.LocalDateTime;

@Slf4j
@Component
@RequiredArgsConstructor
public class OutboundSortExceptionProcessor implements MissionProcessor {

    private final AgvTaskDispatcher getPutTaskDispatcher;
    private final TransactionTemplate transactionTemplate;
    private final WcsMissionDao wcsMissionDao;
    private final WcsResource wcsResource;
    private final IdWorker idWorker;
    private final MissionProcessorCommonUtil commonUtil;
    private final OutboundTaskRecordDao outboundTaskRecordDao;
    private final WcsParamConfigDao wcsParamConfigDao;
    private final Storage1FLocker storage1FLocker;
    private final SmallStorageDao smallStorageDao;
    private final OutboundTaskRecordHistoryDao outboundTaskRecordHistoryDao;
    private final StorageDao storageDao;
    private final OutboundStorage5Or6FloorLocker outboundStorage5Or6FloorLocker;
    private final PalletDao palletDao;
    private final ShiftingStorage5Or6FloorLocker shiftingStorage5Or6FloorLocker;
    private final InboundStorage5Or6FloorLocker inboundStorage5Or6FloorLocker;

    private final Object lock = new Object();

    @Override
    public MissionType getMissionType() {
        return MissionType.OUTBOUND_SORT_EXCEPTION;
    }

    @Override
    public void process(WcsMission missionInfo) {
        OutboundSortExceptionStatus missionStatus = (OutboundSortExceptionStatus) missionInfo.getEnumMissionStatus();

        switch (missionStatus) {
            case WAIT_FOR_STORAGE:
                searchStorage(missionInfo);
                break;
            case SEARCHING_STORAGE:
                break;
            case WAIT_FOR_DISPATCH:
                dispatchAgvTask(missionInfo);
                break;
            case DISPATCHING:
                break;
            case PROCESSING:
                monitorMission(missionInfo);
                break;
            case FINISH:
                finishWcsMission(missionInfo);
                break;
            case CANCEL:
                break;
        }
    }

    public OutboundTaskRecord getOutboundTaskRecord(String recordId) {
        return outboundTaskRecordDao.selectByOutboundRecordId(recordId);
    }


    private void searchStorage(WcsMission missionInfo) {
        boolean statusChangeSearchStorage = statusChangeSearchStorage(missionInfo.getId());
        if (!statusChangeSearchStorage) {
            return;
        }

        //TODO: add lock
        Boolean res = false;
        synchronized (StorageLockCollector.lockStorage5FOr6F) {
            synchronized (StorageLockCollector.lockRestStorage5FOr6F) {

                res = transactionTemplate.execute(status -> {
                    try {
                        OutboundTaskRecord outboundTaskRecord = getOutboundTaskRecord(missionInfo.getAssociatedDetailId());
                        String originalSmallStorage = outboundTaskRecord.getOriginalSmallStorage();

                        boolean isFindStorage5Or6F = true;
                        if (originalSmallStorage != null) {
                            StorageType storageType = storageDao.selectStorageTypeBySmallStorage(originalSmallStorage);
                            if (StorageType.MANTISSA_AREA_5F == storageType) {
                                isFindStorage5Or6F = false;
                            }
                        }

                        if (isFindStorage5Or6F) {
                            Pallet pallet = palletDao.selectByPalletCode(outboundTaskRecord.getPalletCode());
                            SmallStorage smallStorage = smallStorageDao.selectByPallet(pallet.getPalletCode());
                            Tuple<String> lockStorage = shiftingStorage5Or6FloorLocker.lockInStorage(
                                    pallet.getMaterialCode(),
                                    pallet.getInboundOrderCode(),
                                    pallet.getBatchCode(),
                                    outboundTaskRecord.getPalletCode(),
                                    smallStorage.getMediumStorageCode()
                            );

                            if (!lockStorage.success) {
                                return false;
                            }

                            outboundStorage5Or6FloorLocker.lockDestinationStorage(lockStorage.value, outboundTaskRecord.getPalletCode());
                            outboundTaskRecord.setDestinationFloorNum(5);
                            outboundTaskRecord.setDestinationSmallStorage(lockStorage.value);
                            outboundTaskRecordDao.update(outboundTaskRecord);
                            return true;
                        } else {
                            String nullSmallStorage = storageDao.selectNullStorageByStoargeType(StorageType.MANTISSA_AREA_5F);
                            if (nullSmallStorage == null) return false;
                            outboundStorage5Or6FloorLocker.lockDestinationStorage(nullSmallStorage, outboundTaskRecord.getPalletCode());
                            outboundTaskRecord.setDestinationFloorNum(5);
                            outboundTaskRecord.setDestinationSmallStorage(nullSmallStorage);
                            outboundTaskRecordDao.update(outboundTaskRecord);
                            return true;
                        }

                    } catch (Exception e) {
                        log.error("wcs任务{}，OUTBOUND_SORT_EXCEPTION 储位锁定失败", missionInfo.getMissionId(), e);
                        status.setRollbackOnly();
                    }
                    return false;
                });

            }
        }


        if (Boolean.TRUE.equals(res)) {
            wcsMissionDao.updateMissionStatusById(
                    missionInfo.getId()
                    , OutboundSortExceptionStatus.WAIT_FOR_DISPATCH.name()
            );
        } else {
            wcsMissionDao.updateMissionStatusById(
                    missionInfo.getId()
                    , OutboundSortExceptionStatus.WAIT_FOR_STORAGE.name()
            );
        }


    }

    public boolean statusChangeSearchStorage(Integer missionId) {
        if (OutboundSortExceptionStatus.WAIT_FOR_STORAGE == wcsMissionDao.selectMissionStatusById(missionId)) {
            synchronized (lock) {
                if (OutboundSortExceptionStatus.WAIT_FOR_STORAGE == wcsMissionDao.selectMissionStatusById(missionId)) {
                    return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
                        try {
                            wcsMissionDao.updateMissionStatusById(missionId, OutboundSortExceptionStatus.SEARCHING_STORAGE.name());
                            wcsMissionDao.updateStartTime(missionId, LocalDateTime.now());
                            return true;
                        } catch (Exception e) {
                            log.error("wcs任务状态转换出错", e);
                            status.setRollbackOnly();
                        }
                        return false;
                    }));
                }
            }
        }
        return false;
    }


    public boolean statusChangeDISPATCHING(Integer missionId) {
        if (OutboundSortExceptionStatus.WAIT_FOR_DISPATCH == wcsMissionDao.selectMissionStatusById(missionId)) {
            synchronized (lock) {
                if (OutboundSortExceptionStatus.WAIT_FOR_DISPATCH == wcsMissionDao.selectMissionStatusById(missionId)) {
                    return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
                        try {
                            wcsMissionDao.updateMissionStatusById(missionId, OutboundSortExceptionStatus.DISPATCHING.name());
                            return true;
                        } catch (Exception e) {
                            log.error("wcs任务状态转换出错", e);
                            status.setRollbackOnly();
                        }
                        return false;
                    }));
                }
            }
        }
        return false;
    }


    public void dispatchAgvTask(WcsMission missionInfo) {
        if (!statusChangeDISPATCHING(missionInfo.getId())) return;

        OutboundTaskRecord outboundTaskRecord = getOutboundTaskRecord(missionInfo.getAssociatedDetailId());


        wcsResource.getExecutor().execute(() -> {
            synchronized (StorageLockCollector.lockRestStorage5FOr6F) {


                Boolean executeRes = transactionTemplate.execute(status -> {
                    try {
                        // 借用
                        Tuple<PointAndPass> lockAmend = inboundStorage5Or6FloorLocker.lockAmend(outboundTaskRecord.getDestinationSmallStorage());
                        outboundTaskRecordDao.updateDestinationStorageByRecordId(outboundTaskRecord.getOutboundRecordId(), lockAmend.value.getPointStorage());


                        String agvTaskId = "Task_Agv_" + idWorker.nextId();
                        AgvTaskSource agvTaskSource = new AgvTaskSource(agvTaskId, AgvMissionType.OUTBOUND_TYPE, 1, missionInfo.getMissionId(), missionInfo.getMissionType(),
                                OutboundSortExceptionStatus.PROCESSING.name(), outboundTaskRecord.getPalletCode(), null, lockAmend.value.getPassStorage());
                        agvTaskSource.addAction(new AgvActionSource(outboundTaskRecord.getStartSmallStorage(), AgvAction.GET))
                                .addAction(new AgvActionSource(lockAmend.value.getPassStorage(), AgvAction.PUT));


                        if (getPutTaskDispatcher.dispatch(agvTaskSource)) {
                            wcsMissionDao.updateMissionStatusById(missionInfo.getId(), OutboundSortExceptionStatus.PROCESSING.name());
                        } else {
                            wcsMissionDao.updateMissionStatusById(missionInfo.getId(), OutboundSortExceptionStatus.WAIT_FOR_DISPATCH.name());
                        }
                        return true;
                    } catch (Exception e) {
                        log.error("wcs任务：{}，生成agv任务失败", missionInfo.getMissionId(), e);
                        status.setRollbackOnly();
                    }
                    return false;
                });

                if (Boolean.FALSE.equals(executeRes)) {
                    wcsMissionDao.updateMissionStatusById(missionInfo.getId(), OutboundSortExceptionStatus.WAIT_FOR_DISPATCH.name());
                }

            }
        });
    }


    public void monitorMission(WcsMission missionInfo) {

    }

    public void finishWcsMission(WcsMission missionInfo) {
        transactionTemplate.executeWithoutResult(status -> {
            try {
                OutboundTaskRecord outboundTaskRecord = getOutboundTaskRecord(missionInfo.getAssociatedDetailId());
                if (outboundTaskRecord.getOutboundOrderCode() == null) {
                    outboundTaskRecordHistoryDao.insert(
                            new OutboundTaskRecordHistory(null, outboundTaskRecord.getOutboundRecordId(), outboundTaskRecord.getOutboundOrderCode(),
                                    outboundTaskRecord.getOutboundOrderDetailId(), outboundTaskRecord.getPalletCode(), outboundTaskRecord.getMaterialCode(),
                                    outboundTaskRecord.getMaterialNum(), outboundTaskRecord.getStartFloorNum(), outboundTaskRecord.getStartSmallStorage(),
                                    outboundTaskRecord.getDestinationFloorNum(), outboundTaskRecord.getDestinationSmallStorage(),
                                    outboundTaskRecord.getIsSort(), outboundTaskRecord.getOriginalPalletMaterialNum(), outboundTaskRecord.getCreateTime(),
                                    outboundTaskRecord.getOriginalSmallStorage(), outboundTaskRecord.getLogisticsCompanyCode())
                    );
                    outboundTaskRecordDao.deleteById(outboundTaskRecord.getId());
                }

                missionInfo.setFinishTime(LocalDateTime.now());
                commonUtil.transformMissionHistory(missionInfo);
            } catch (Exception e) {
                log.error("任务结束错误", e);
                status.setRollbackOnly();
            }
        });

    }
}
